package main

/*
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <time.h>
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>

int isclosed(int sock) {
  fd_set rfd;
  FD_ZERO(&rfd);
  FD_SET(sock, &rfd);
  struct timeval tv = { 0 };
  select(sock+1, &rfd, 0, 0, &tv);
  if (!FD_ISSET(sock, &rfd))
    return 0;
  int n = 0;
  ioctl(sock, FIONREAD, &n);
  return n == 0;
}
static void chksock(int sock) {
    int ka = 0;
    socklen_t kalen = sizeof(ka);
    int a1 = getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &ka, &kalen);
    printf("ka: %d, %d, %d, %d\n", a1, ka, kalen, errno);
    // TCP_KEEPCNT, TCP_KEEPIDLE, and TCP_KEEPINTVL
    int kc = 0;
    socklen_t kclen = sizeof(kc);
    int a2 = getsockopt(sock, SOL_SOCKET, TCP_KEEPCNT, &kc, &kclen);
    printf("kc: %d, %d, %d, %d\n", a1, kc, kclen, errno);

    ka = 1;
    a1 = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka));
    printf("ka: %d, %d, %d, %d\n", a1, ka, kalen, errno);
    ka = 0;
    kalen = sizeof(ka);
    a1 = getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &ka, &kalen);
    printf("ka: %d, %d, %d, %d\n", a1, ka, kalen, errno);
    int ki = 0;
    int kilen = sizeof(ki);
    a1 = getsockopt(sock, SOL_SOCKET, TCP_KEEPINTVL, &ki, &kilen);
    printf("ka: %d, %d, %d, %d\n", a1, ki, kilen, errno);
}

int setKeepAlive(int fd, int interval)
{
    int val = 1;

    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1) {
        printf("setsockopt SO_KEEPALIVE: %s", strerror(errno));
        return -1;
    }

    // Send first probe after `interval' seconds.
    val = interval;
    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
        printf("setsockopt TCP_KEEPIDLE: %s\n", strerror(errno));
        return -1;
    }

    // * Send next probes after the specified interval. Note that we set the
    // * delay as interval / 3, as we send three probes before detecting
    // * an error (see the next setsockopt call). * /
    val = interval/3;
    if (val == 0) val = 1;
    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
        printf("setsockopt TCP_KEEPINTVL: %s\n", strerror(errno));
        return -1;
    }

    // * Consider the socket in error state after three we send three ACK
    // * probes without getting a reply. * /
    val = 3;
    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
        printf("setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
        return -1;
    }

    return 0;
}

*/
import "C"

import (
	"log"
	"os"
	// "syscall"
	"time"

	"go-purple/gloox-swig"
	"mkuse/hlpbot"
)

func main() {
	jid := gloox.NewJID(username)
	log.Println(jid)
	cli := gloox.NewClient(jid, password)
	log.Println(cli)
	assit := helper.NewAssistant()
	helper.SetIp2RegionDb(os.Getenv("HOME") + "/golib/src/github.com/mohong122/ip2region/data/ip2region.db")

	sendMessage := func(msg string) {
		jido := gloox.NewJID(peername)
		msgo := gloox.NewMessage(gloox.MessageNormal, jido,
			"re: "+msg, "hehhe")
		cli.Send(msgo)
		gloox.DeleteJID(jido)
		gloox.DeleteMessage(msgo)
	}

	// async get assit's result notification
	go func() {
		for res := range assit.ResultC() {
			sendMessage(res.Result)
		}
		log.Println("assit result handler routine done")
	}()

	var msgh = gloox.NewMessageHandlerX()
	msgh.HandlerMessageX = func(msg gloox.Message, session gloox.MessageSession) {
		stanza := gloox.SwigcptrStanza(msg.Swigcptr())
		log.Println("msg:", msg, session,
			stanza.From().Full(),
			stanza.To().Full(), "//",
			msg.Subtype(),
			msg.Body(), msg.Subject(),
			msg.Thread())
		// cli.Send(gloox.NewMessage(msg.Subtype(), gloox.NewJID("yatseni@xmpp.jp"), "re:"+msg.Body(), "hehhe"))
		assit.MaybeCmdAsync(msg.Body(), cli)
		/*
			cmdrs := assit.MaybeCmd(msg.Body())
			for _, r := range cmdrs {
				sendMessage(r)
			}
		*/
	}
	cli.RegisterMessageHandler(msgh)
	var conlsn = gloox.NewConnectionListerX()
	conlsn.OnConnectX = func() {
		log.Println("Connected")

	}
	conlsn.OnDisconnectX = func(error int) {
		stmerror := cli.StreamError()
		stmerrmsg := cli.StreamErrorText()
		log.Println("Disconnected:", error, stmerror, stmerrmsg)
		switch gloox.GlooxConnectionError(error) {
		case gloox.ConnIoError:
			cli.Disconnect()
		}
	}
	conlsn.OnTLSConnectX = func(info gloox.CertInfo) {
		log.Println("OnTLSConnectX:", info.GetProtocol(),
			info.GetCipher(), info.GetCompression(),
			info.GetServer())
	}
	cli.RegisterConnectionListener(conlsn)
	var logh = gloox.NewLogHandlerX()
	logh.HandleLogX = func(level int, area int, s string) {
		// log.Println("log", level, area, s)
	}
	cli.LogInstance().RegisterLogHandler(gloox.LogLevelDebug, int(gloox.LogAreaAll), logh)
	log.Println(cli.LogInstance())

	var roomcreated = false
	var presh = gloox.NewPresenceHandlerX()
	presh.HandlePresenceX = func(ptype int, from, to, status string) {
		log.Println(ptype, from, to, status)
		// it's just peer Unavailable
		if ptype == int(gloox.PresenceUnavailable) {
			log.Println("should reconnect?")
			// cli.Disconnect()
			// return
		}
		if roomcreated {
			return
		}
		var roomo gloox.MUCRoom
		roomcreated = true

		var roomh = gloox.NewMUCRoomHandlerX()
		roomh.HandleMUCParticipantPresenceX = func(room gloox.MUCRoom, part gloox.MUCRoomParticipant, presence gloox.Presence) {
			log.Println("muc part pres:", room, part, presence)
			log.Println("muc part pres:", room, part.GetJid(), presence)
			log.Println("muc part pres:", presence.Presence())
			roomo.SetSubject("subjectwhat嗯")
		}
		roomh.HandleMUCMessageX = func(room gloox.MUCRoom, msg gloox.Message, priv bool) {
			log.Println("muc msg:", msg.Body(), "priv:", priv)
		}
		roomh.HandleMUCRoomCreationX = func(room gloox.MUCRoom) {
			log.Println("room creation")
			roomo.RequestRoomConfig()
			roomo.RequestList(0)
		}
		roomh.HandleMUCSubjectX = func(room gloox.MUCRoom, nick, subject string) {
			log.Println("muc subject:", nick, subject)
		}
		roomh.HandleMUCInviteDeclineX = func(room gloox.MUCRoom, jid, reason string) {
			log.Println("muc inv decline:", jid, reason)
		}
		roomh.HandleMUCErrorX = func(room gloox.MUCRoom, error int) {
			log.Println("muc error:", error)
		}
		roomh.HandleMUCInfoX = func(room gloox.MUCRoom, features int, name string) {
			log.Println("muc info:", features, name)
		}
		roomh.HandleMUCItemsX = func(room gloox.MUCRoom) {
			log.Println("muc items:")
		}

		var roomcfgh = gloox.NewMUCRoomConfigHandlerX()
		roomcfgh.HandleMUCConfigListX = func(arg2 gloox.MUCRoom, operation int /*, arg3 Std_list_Sl_gloox_MUCListItem_Sg_, arg4 GlooxMUCOperation*/) {
			log.Println("muc cfg list:", operation)
		}
		roomcfgh.HandleMUCConfigFormX = func(arg2 gloox.MUCRoom, form gloox.DataForm /*, arg3 DataForm*/) {
			log.Println("muc cfg form:", form.Title())
			// nform := gloox.NewDataForm(gloox.GlooxFormType(1), form.Title())
			nform := gloox.SwigcptrDataForm(form.Clone().Swigcptr())
			// nform = gloox.SwigcptrDataForm(gloox.NewDataForm(form.Xtype(), form.Title()).Swigcptr())
			//*
			fieldx := nform.Field("muc#roomconfig_publicroom")
			log.Println(fieldx)
			fieldx.SetValue("0")
			fieldx = nform.Field("muc#roomconfig_roomname")
			log.Println(fieldx)
			fieldx.SetValue("nameviacfg")
			fieldx = nform.Field("public_list")
			log.Println(fieldx)
			fieldx.SetValue("0")
			fieldx = nform.Field("muc#roomconfig_enablelogging")
			log.Println(fieldx)
			// can not add this field
			if fieldx.Swigcptr() == 0 {
				// nform.AddField(gloox.DataFormFieldTypeBoolean, "muc#roomconfig_enablelogging", "0")
			} else {
				// fieldx.SetValue("0")
			}
			fieldx = nform.Field("muc#roomconfig_passwordprotectedroom")
			log.Println(fieldx)
			fieldx.SetValue("1")
			fieldx = nform.Field("muc#roomconfig_roomsecret")
			log.Println(fieldx)
			fieldx.SetValue("123456")

			if true {
				nform.SetType(1)
				// nform = gloox.SwigcptrDataForm(gloox.RefillDataForm(form.Swigcptr()))
				roomo.SetRoomConfig(nform)
				// gloox.DeleteDataForm(nform) // delete too early
			}
			//*/
			// TODO crash
		}
		roomcfgh.HandleMUCConfigResultX = func(arg2 gloox.MUCRoom, arg3 bool, operation int /*, arg4 GlooxMUCOperation*/) {
			log.Println("muc cfg result:", arg3, operation)
		}
		roomcfgh.HandleMUCRequestX = func(arg2 gloox.MUCRoom, form gloox.DataForm /*, arg3 DataForm*/) {
			log.Println("muc request:", form.Title())
		}

		// jid后缀部分很重要，否则会:That nickname is registered by another person
		roomo = gloox.NewMUCRoom(cli, gloox.NewJID("testgloox123456xxx@conference.xmpp.jp/goglooxhehehehhe123456"), roomh, roomcfgh)
		// roomo.RegisterMUCRoomHandler(roomh)
		// roomo.RegisterMUCRoomConfigHandler(roomcfgh)
		roomo.SetPresence(gloox.PresenceAvailable)
		roomo.SetPublish(false, false)
		roomo.SetSubject("trsubn哈") // set too early
		roomo.SetNick("nickwhat")

		// roomo.RequestRoomConfig()
		roomo.Join()
		roomo.Invite(gloox.NewJID("yatseni@xmpp.jp"), "hohotest", "tttttt")

		refobj(roomo)
		log.Println(roomo, roomh, roomcfgh, roomo.Name(), roomo.Flags(),
			roomo.Nick())
	}
	cli.RegisterPresenceHandler(presh)

	fuzz := gloox.NewAttention()
	fuzzx := gloox.SwigcptrStanzaExtension(fuzz.Swigcptr())
	cli.RegisterStanzaExtension(fuzzx)

	disco := cli.Disco()
	disco.AddFeature(gloox.GetXMLNS_ATTENTION())
	disco.AddFeature(gloox.GetXMLNS_MUC())
	disco.AddFeature(gloox.GetXMLNS_MUC_ADMIN())
	disco.AddFeature(gloox.GetXMLNS_MUC_OWNER())
	disco.AddFeature(gloox.GetXMLNS_MUC_REQUEST())
	disco.AddFeature(gloox.GetXMLNS_MUC_ROOMINFO())
	disco.AddFeature(gloox.GetXMLNS_MUC_ROOMS())
	disco.AddFeature(gloox.GetXMLNS_MUC_UNIQUE())
	disco.AddFeature(gloox.GetXMLNS_MUC_USER())
	disco.AddFeature(gloox.GetXMLNS_X_OOB())
	disco.AddFeature(gloox.GetXMLNS_DISCO_INFO())
	disco.AddFeature(gloox.GetXMLNS_DISCO_ITEMS())
	disco.AddFeature(gloox.GetXMLNS_DISCO_PUBLISH())
	disco.AddFeature(gloox.GetXMLNS_CHAT_STATES())
	disco.AddFeature(gloox.GetXMLNS_XMPP_PING())
	disco.AddFeature(gloox.GetXMLNS_XMPP_BOSH())
	disco.AddFeature(gloox.GetXMLNS_XMPP_STANZAS())
	disco.AddFeature(gloox.GetXMLNS_XMPP_STREAM())

	oob := gloox.NewOOB("https://www.xmpp.jp/captcha/15942216777261419697", "heihei", false)
	oobx := gloox.SwigcptrStanzaExtension(oob.Swigcptr())
	cli.RegisterStanzaExtension(oobx)
	cli.SetStreamManagement(true, true)
	rosterm := cli.RosterManager()
	log.Println(rosterm)

	evth := gloox.NewEventHandlerX()
	evth.HandleEventX = func(evt gloox.Event) {
		// log.Println("evt:", evt.EventType(), evt.Stanza())
		switch evt.EventType() {
		case gloox.EventPingPing:
		case gloox.EventPingPong:
		case gloox.EventPingError:
			log.Println("evt:", evt.EventType(), evt.Stanza())
			log.Println("maybe need reconnect")
			break
		}
	}

	if false {
		// lastConnTime := time.Now()
		ok := cli.Connect(false)
		log.Println(ok)
		connx := cli.ConnectionImpl()
		conn := gloox.SwigcptrConnectionTCPClient(connx.Swigcptr())
		log.Println(conn.Socket())
		// time.Sleep(10 * time.Second)
		go func() {
			sock := conn.Socket()
			for {
				isc := C.isclosed(C.int(sock))
				// log.Println("closed:", isc)
				if isc == 0 {
					time.Sleep(500 * time.Millisecond)
					continue
				} else {
					log.Println("closed")
					break
				}
			}

		}()
	}

	pingproc := func() {
		for {
			// now = time.Now()
			// if now.Sub(lastPingTime).Seconds() > 10 {
			// 	lastPingTime = now
			// 	// cli.XmppPing(gloox.NewJID(peername), nil)
			// jid := gloox.NewJID(cli.Jid().Bare())
			jid := cli.Jid()
			cli.XmppPing(jid, evth)
			// 	jid := gloox.NewJID(dummyjid)
			// cli.XmppPing(jid, evth)
			// cli.WhitespacePing()
			// log.Println("pinged", cli.Jid().Bare())
			// gloox.DeleteJID(jid)
			time.Sleep(15 * time.Second)
			// }
		}
	}
	go pingproc()

	// lastPingTime := time.Now()
	reconnTimes := 0
	for {
		log.Println("Connecting...", gloox.SwigEnumAsInt(cli.State()))
		cok := cli.Connect(false)
		if !cok {
			// becontinue:
			reconnTimes += 1
			log.Println("reconnect after 3 seconds...", reconnTimes)
			time.Sleep(3 * time.Second)
			continue
		}
		connx := cli.ConnectionImpl()
		conn := gloox.SwigcptrConnectionTCPClient(connx.Swigcptr())
		// log.Println(conn.Socket())
		// C.chksock(C.int(conn.Socket()))
		C.setKeepAlive(C.int(conn.Socket()), 20)

		log.Println("Recving...", gloox.SwigEnumAsInt(cli.State()))
		for {
			err := cli.Recv(2 * 1000000) // it's microsecond
			ierr := gloox.GlooxConnectionError(SwigEnum(err.Swigcptr()))
			gloox.DeleteSwigEnum(err)
			switch ierr {
			case gloox.ConnNoError:
				// log.Println("ConnNoError") // timeout is no error
			case gloox.ConnNotConnected:
				log.Println("ConnNotConnected")
			case gloox.ConnIoError:
				log.Println("ConnIoError")
			default:
				log.Println("recv error:", err, err.Swigcptr(), ierr)
			}
			if ierr != gloox.ConnNoError {
				break // becontinue, 不能像后break
			}
		}
		// time.Sleep(100 * time.Millisecond)
	}

	log.Println("main loop finished.")
	select {}
}

/*
能够接收 recv ConnNotConnected,
能够接收 Disconnected事件,
能够执行重连接到TLSConnected,
但是，就是连接不上，这是个啥bug呢
因此，加了以上几行代码，测试一下看看。
*/
